﻿using System.Diagnostics;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Windows;

namespace TcpTool
{
    /// <summary>
    /// Tcp服务端
    /// </summary>
    public class TCPServer
    {
        private TcpListener? tcpListener;
        private readonly List<TcpClient> clients;
        private bool isListening;

        public Action<bool>? IsOpenServer;  // 服务是否开启
        public Action<TcpClient>? ClientConnected; // 当前新增客户端
        public Action<TcpClient, byte[]>? DataReceived; // 接收到数据

        public TCPServer()
        {
            clients = new();
        }

        /// <summary>
        /// 打开服务
        /// </summary>
        /// <param name="serverIP">ip</param>
        /// <param name="port">端口</param>
        /// <param name="isActionReceiveData">是否通过委托接受数据</param>
        /// <returns></returns>
        public async Task OpenServerAsync(IPAddress serverIP, int port, bool isActionReceiveData = true)
        {
            #region 打开服务

            try
            {
                tcpListener = new TcpListener(serverIP, port);
                tcpListener.Start();
                isListening = true;
                IsOpenServer?.Invoke(true);

                while (isListening)
                {
                    try
                    {
                        TcpClient client = await Task.Run(() => tcpListener.AcceptTcpClientAsync()).ConfigureAwait(false);
                        clients.Add(client);

                        UpdateClientsList();
                        ClientConnected?.Invoke(client);

                        if (isActionReceiveData)
                        {
                            // 开始接收客户端数据
                            _ = Task.Run(() => ReceiveData(client));
                        }

                        //注意：如果不使用委托方式接收回文，而是下面发送数据后就读回文，
                        //则需要注释掉上面的接收数据代码，这种方式需要指定接收数据的长度
                        //List<byte> byteLists = [0x55, 0xD5, 0xCD, 0xC2, 0x00, 0x0A, 0x40, 0x00, 0x30, 0x08, 0x00, 0x04, 0x15];
                        //_ = server?.SendDataAsync(tcpClient, [.. byteLists]);
                        //Thread.Sleep(500);
                        //byte[] bytes = new byte[15];
                        //tcpClient?.GetStream().ReadAsync(bytes, 0, bytes.Length);
                    }
                    catch (SocketException)
                    {
                        IsOpenServer?.Invoke(false);
                    }
                    catch (OperationCanceledException)
                    {
                        IsOpenServer?.Invoke(false);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                IsOpenServer?.Invoke(false);
                await Console.Out.WriteLineAsync(ex.ToString());
            }

            #endregion
        }

        /// <summary>
        /// 接收数据
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        private async Task ReceiveData(TcpClient client)
        {
            #region 接收数据

            NetworkStream stream = client.GetStream();
            byte[] buffer = new byte[8192]; // 缓冲区，存储接收到的数据
            int bytesRead;

            while ((bytesRead = await stream.ReadAsync(buffer)) > 0)
            {
                byte[] receivedData = new byte[bytesRead];
                Array.Copy(buffer, receivedData, bytesRead);
                DataReceived?.Invoke(client, receivedData); // 以byte[]形式触发事件

                Array.Clear(buffer, 0, buffer.Length); // 清空缓冲区
            }

            #endregion
        }

        /// <summary>
        /// 发送数据到客户端
        /// </summary>
        /// <param name="client"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task SendDataAsync(TcpClient client, byte[] data)
        {
            #region 发送数据到客户端

            try
            {
                if (client == null || !client.Connected) return;

                NetworkStream stream = client.GetStream();
                await stream.WriteAsync(data).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            #endregion
        }

        /// <summary>
        /// 发送数据到客户端
        /// </summary>
        /// <param name="client"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public async Task SendDataAsync(string ip, byte[] data)
        {
            #region 发送数据到客户端

            try
            {
                //发送给所有客户端
                if (ip == "ALL")
                {
                    if (clients.Count > 0)
                    {
                        foreach (var client in clients)
                        {
                            NetworkStream stream = client.GetStream();
                            await stream.WriteAsync(data).ConfigureAwait(false);
                        }
                    }
                }
                else
                {
                    TcpClient? client = clients.FirstOrDefault(x => ((IPEndPoint)x.Client.RemoteEndPoint!).Address.ToString() == ip);
                    if (client == null || !client.Connected) return;

                    NetworkStream stream = client.GetStream();
                    await stream.WriteAsync(data).ConfigureAwait(false);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            #endregion
        }

        /// <summary>
        /// 关闭服务
        /// </summary>
        public void CloseServer()
        {
            #region 关闭服务

            try
            {
                isListening = false;
                tcpListener?.Stop();

                foreach (TcpClient client in clients)
                {
                    client?.Close();
                }

                clients?.Clear();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            #endregion
        }

        /// <summary>
        /// 更新客户端列表
        /// </summary>
        private void UpdateClientsList()
        {
            #region 更新客户端列表

            try
            {
                List<TcpClient> disconnectedClients = new();

                //添加连接的客户端
                foreach (TcpClient client in clients.ToList())
                {
                    if (!IsClientConnected(client))
                    {
                        disconnectedClients.Add(client);
                    }
                }

                //移出已断开连接的客户端
                foreach (TcpClient disconnectedClient in disconnectedClients)
                {
                    clients.Remove(disconnectedClient);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            #endregion
        }

        /// <summary>
        /// 获取在线客户端集合
        /// </summary>
        /// <returns></returns>
        public List<TcpClient>? GetClients()
        {
            #region 获取在线客户端集合

            try
            {
                List<TcpClient> onlineClients = new();

                // 临时集合，用来存储需要移除的客户端
                List<TcpClient> clientsToRemove = new();

                foreach (TcpClient client in clients.ToList())
                {
                    if (IsClientConnected(client))
                        onlineClients.Add(client);
                    else
                        clientsToRemove.Add(client);
                }

                // 移除已断开连接的客户端
                foreach (TcpClient clientToRemove in clientsToRemove)
                {
                    clients.Remove(clientToRemove);
                }

                //Console.WriteLine($"{string.Join("\n", onlineClients.Select(x => ((IPEndPoint)x.Client.RemoteEndPoint!).Address.ToString()))}");
                return onlineClients;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return null;
            }

            #endregion
        }

        /// <summary>
        /// 获取在线客户端IP集合
        /// </summary>
        /// <returns></returns>
        public List<IPEndPoint>? GetClientIPEndPoints()
        {
            #region 获取在线客户端IP集合

            try
            {
                List<IPEndPoint> IPEndPointList = new();

                foreach (TcpClient client in clients)
                {
                    if (IsClientConnected(client))
                    {
                        IPEndPoint clientIp = (IPEndPoint)client.Client.RemoteEndPoint!;
                        IPEndPointList.Add(clientIp);
                    }
                }

                return IPEndPointList;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return null;
            }

            #endregion
        }

        /// <summary>
        /// 判断客户端是否连接
        /// </summary>
        /// <param name="client"></param>
        /// <returns></returns>
        private bool IsClientConnected(TcpClient client)
        {
            #region 判断客户端是否连接

            try
            {
                if (client is null || client?.Client is null) return false;

                //如果设备ip地址ping不通表示设备不在线
                IPAddress clientIP = ((IPEndPoint)client?.Client?.RemoteEndPoint!).Address;

                //Ping超时时间从1秒改为3秒.
                //IPStatus iPStatus = new Ping().Send(clientIP, 3000).Status;
                //if (iPStatus != IPStatus.Success) return false;

                if (client.Client.Poll(0, SelectMode.SelectRead))
                {
                    byte[] buff = new byte[1];
                    if (client.Client.Receive(buff, SocketFlags.Peek) == 0)
                    {
                        return false;
                    }
                }

                return true;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
                return false;
            }

            #endregion
        }

        /// <summary>
        /// 断开指定客户端连接
        /// </summary>
        /// <param name="client"></param>
        public void DisconnectClient(TcpClient client)
        {
            #region 断开指定客户端连接

            try
            {
                client.Close();
                clients.Remove(client);
                UpdateClientsList();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            #endregion
        }
    }
}
